#include "hr.hpp"

// 辅助函数：判断点是否在三角形内（使用边缘方程）
bool is_inside_triangle(
    fpal::fp_coord_t x, fpal::fp_coord_t y,
    fpal::fp_coord_t A0, fpal::fp_coord_t B0, fpal::fp_coord_t C0,
    fpal::fp_coord_t A1, fpal::fp_coord_t B1, fpal::fp_coord_t C1,
    fpal::fp_coord_t A2, fpal::fp_coord_t B2, fpal::fp_coord_t C2
) {
    #pragma HLS INLINE 
    fpal::fp_coord_t w0 = fpal::add_fx(fpal::mul_fx(A0, x), fpal::add_fx(fpal::mul_fx(B0, y), C0));
    fpal::fp_coord_t w1 = fpal::add_fx(fpal::mul_fx(A1, x), fpal::add_fx(fpal::mul_fx(B1, y), C1));
    fpal::fp_coord_t w2 = fpal::add_fx(fpal::mul_fx(A2, x), fpal::add_fx(fpal::mul_fx(B2, y), C2));

    bool inside = (w0 >= 0 && w1 >= 0 && w2 >= 0);

    return inside;
}

// 辅助函数：更新Bounding Box
void update_bounding_box(
    fpal::fp_coord_t vx, fpal::fp_coord_t vy,
    fpal::fp_screen_pixel_idx_t& min_x, fpal::fp_screen_pixel_idx_t& max_x,
    fpal::fp_screen_pixel_idx_t& min_y, fpal::fp_screen_pixel_idx_t& max_y,
    const ap_uint<11> max_x_res, const ap_uint<11> max_y_res
) {
    #pragma HLS INLINE
    // max_x_res - 1 确保在 fp_coord_t 域进行减法，避免 ap_uint 下溢风险
    fpal::fp_coord_t max_x_res_fx = fpal::to_fp_coord(max_x_res);
    fpal::fp_coord_t max_y_res_fx = fpal::to_fp_coord(max_y_res);

    fpal::fp_screen_pixel_idx_t cur_x = fpal::to_fp_screen_pixel_idx(fpal::clamp(vx, fpal::to_fp_coord(0), fpal::sub_fx(max_x_res_fx, fpal::to_fp_coord(1))));
    fpal::fp_screen_pixel_idx_t cur_y = fpal::to_fp_screen_pixel_idx(fpal::clamp(vy, fpal::to_fp_coord(0), fpal::sub_fx(max_y_res_fx, fpal::to_fp_coord(1))));

    if (cur_x < min_x) min_x = cur_x;
    if (cur_x > max_x) max_x = cur_x;
    if (cur_y < min_y) min_y = cur_y;
    if (cur_y > max_y) max_y = cur_y;
}


// 辅助函数：处理单个三角形的像素
void process_pixels_for_triangle(
    const raster_params_packet_fixed& current_triangle_params,
    hls::stream<pixel_fragment_packet_fixed>& output_pixel_fragment_stream,
    fpal::fp_screen_pixel_idx_t min_x, fpal::fp_screen_pixel_idx_t max_x,
    fpal::fp_screen_pixel_idx_t min_y, fpal::fp_screen_pixel_idx_t max_y,
    fpal::fp_coord_t v0_x, fpal::fp_coord_t v0_y, fpal::fp_z_buffer_t v0_z_val,
    fpal::fp_coord_t dzdx, fpal::fp_coord_t dzdy,
    ap_uint<32>& fragment_counter // 用于计数
) {
    #pragma HLS INLINE OFF 
    
    Y_LOOP: for (fpal::fp_screen_pixel_idx_t y = min_y; y <= max_y; ++y) {
        #pragma HLS LOOP_FLATTEN 
        #pragma HLS LOOP_TRIPCOUNT min=1 max=720 
        X_LOOP: for (fpal::fp_screen_pixel_idx_t x = min_x; x <= max_x; ++x) {
            #pragma HLS LOOP_TRIPCOUNT min=1 max=1280 
            #pragma HLS PIPELINE II=1 

            pixel_fragment_packet_fixed fragment;
            fragment.screen_x = x;
            fragment.screen_y = y;
            fragment.pure_color_R = current_triangle_params.pure_color_R;
            fragment.pure_color_G = current_triangle_params.pure_color_G;
            fragment.pure_color_B = current_triangle_params.pure_color_B;
            fragment.is_valid_pixel = false; 

            fpal::fp_coord_t pixel_x_coord = fpal::to_fp_coord(x);
            fpal::fp_coord_t pixel_y_coord = fpal::to_fp_coord(y);

            if (is_inside_triangle(
                    pixel_x_coord, pixel_y_coord,
                    current_triangle_params.edge0_A, current_triangle_params.edge0_B, current_triangle_params.edge0_C,
                    current_triangle_params.edge1_A, current_triangle_params.edge1_B, current_triangle_params.edge1_C,
                    current_triangle_params.edge2_A, current_triangle_params.edge2_B, current_triangle_params.edge2_C
                )) {
                
                fragment.is_valid_pixel = true;

                // 深度插值 (线性插值)
                fpal::fp_coord_t current_z_coord_val_fx = fpal::to_fp_coord(v0_z_val); 

                fpal::fp_coord_t delta_x = fpal::sub_fx(pixel_x_coord, v0_x);
                fpal::fp_coord_t delta_y = fpal::sub_fx(pixel_y_coord, v0_y);

                fpal::fp_coord_t dz_offset_x = fpal::mul_fx(dzdx, delta_x);
                fpal::fp_coord_t dz_offset_y = fpal::mul_fx(dzdy, delta_y);

                fpal::fp_coord_t interpolated_z_fx = fpal::add_fx(current_z_coord_val_fx, fpal::add_fx(dz_offset_x, dz_offset_y));
                
                // 将插值后的深度值转换回 fp_z_buffer_t 类型 (U.16.0)
                fragment.interpolated_z = fpal::to_fp_z_buffer(interpolated_z_fx);
            }

            output_pixel_fragment_stream.write(fragment);
            fragment_counter++; 
        } 
    } 
}


void hardware_rasterizer(
    hls::stream<raster_params_packet_fixed>& input_raster_params_stream,
    hls::stream<pixel_fragment_packet_fixed>& output_pixel_fragment_stream,
    const ap_uint<11> max_x_res, 
    const ap_uint<11> max_y_res,  
    const ap_uint<16> num_triangles,
    ap_uint<32>& total_fragments_generated 
) {
    #pragma HLS INTERFACE axis port=input_raster_params_stream
    #pragma HLS INTERFACE axis port=output_pixel_fragment_stream
    #pragma HLS INTERFACE s_axilite port=max_x_res bundle=ctrl_s_axilite
    #pragma HLS INTERFACE s_axilite port=max_y_res bundle=ctrl_s_axilite
    #pragma HLS INTERFACE s_axilite port=num_triangles bundle=ctrl_s_axilite
    #pragma HLS INTERFACE s_axilite port=total_fragments_generated bundle=ctrl_s_axilite 
    #pragma HLS INTERFACE s_axilite port=return bundle=ctrl_s_axilite

    #pragma HLS DATAFLOW
                        
    ap_uint<32> fragment_counter = 0; 

    TRIANGLE_PROCESS_LOOP:
    for (ap_uint<16> triangle_idx = 0; triangle_idx < num_triangles; ++triangle_idx) {
        #pragma HLS LOOP_TRIPCOUNT min=500 max=2000 

        raster_params_packet_fixed current_triangle_params;

        input_raster_params_stream.read(current_triangle_params); 

        fpal::fp_coord_t v0_x = current_triangle_params.v0_x;
        fpal::fp_coord_t v0_y = current_triangle_params.v0_y;
        fpal::fp_z_buffer_t v0_z_val = current_triangle_params.v0_z; 
        fpal::fp_coord_t dzdx = current_triangle_params.dzdx;
        fpal::fp_coord_t dzdy = current_triangle_params.dzdy;

        fpal::fp_screen_pixel_idx_t min_x = fpal::to_fp_screen_pixel_idx(fpal::clamp(v0_x, fpal::to_fp_coord(0), fpal::to_fp_coord(max_x_res - 1)));
        fpal::fp_screen_pixel_idx_t min_y = fpal::to_fp_screen_pixel_idx(fpal::clamp(v0_y, fpal::to_fp_coord(0), fpal::to_fp_coord(max_y_res - 1)));
        fpal::fp_screen_pixel_idx_t max_x = min_x;
        fpal::fp_screen_pixel_idx_t max_y = min_y;

        // 调用辅助函数更新Bounding Box
        update_bounding_box(current_triangle_params.v1_x, current_triangle_params.v1_y, 
                            min_x, max_x, min_y, max_y, max_x_res, max_y_res);
        update_bounding_box(current_triangle_params.v2_x, current_triangle_params.v2_y, 
                            min_x, max_x, min_y, max_y, max_x_res, max_y_res);
        
        // 确保 Bounding Box 在屏幕范围内
        fpal::fp_coord_t max_x_res_fx = fpal::to_fp_coord(max_x_res);
        fpal::fp_coord_t max_y_res_fx = fpal::to_fp_coord(max_y_res);

        min_x = fpal::clamp(min_x, (fpal::fp_screen_pixel_idx_t)0, fpal::to_fp_screen_pixel_idx(fpal::sub_fx(max_x_res_fx, fpal::to_fp_coord(1))));
        min_y = fpal::clamp(min_y, (fpal::fp_screen_pixel_idx_t)0, fpal::to_fp_screen_pixel_idx(fpal::sub_fx(max_y_res_fx, fpal::to_fp_coord(1))));
        max_x = fpal::clamp(max_x, (fpal::fp_screen_pixel_idx_t)0, fpal::to_fp_screen_pixel_idx(fpal::sub_fx(max_x_res_fx, fpal::to_fp_coord(1))));
        max_y = fpal::clamp(max_y, (fpal::fp_screen_pixel_idx_t)0, fpal::to_fp_screen_pixel_idx(fpal::sub_fx(max_y_res_fx, fpal::to_fp_coord(1))));

        process_pixels_for_triangle(
            current_triangle_params,
            output_pixel_fragment_stream,
            min_x, max_x, min_y, max_y,
            v0_x, v0_y, v0_z_val, dzdx, dzdy,
            fragment_counter 
        );
    } 

    total_fragments_generated = fragment_counter; 
}

